﻿using System.Diagnostics;
using System.Reflection;
using AutoTest.Common;
using AutoTest.Common.Extension;
using AutoTest.Framework.Module;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Serilog;

namespace AutoTest.Framework
{
    /// <summary>
    /// 框架加载类
    /// </summary>
    public static class FrameBooter
    {
        /// <summary>
        /// 程序的程序及（核心模块，组建模块和业务程序模块）
        /// </summary>
        public static List<ModuleInfo> Assemblies { get; private set; } = [];
        /// <summary>
        /// 所有业务组建模块的总的类
        /// 在自动化映射的时候只会去扫面业务程序模块中的类
        /// 在业务程序模块中找对应的类
        /// </summary>
        public static List<TypeInfo> AllTypes { get; private set; } = [];

        public static void Boot(WebApplicationBuilder builder, Action action)
        {
            var watcher = new Stopwatch();
            watcher.Start();
            Log.Information($"当前环境：{builder.Environment.EnvironmentName}");
            Log.Information("程序启动ing...");
            long prev = 0;
            try
            {
                /*******************************替换日志************************************************/
                builder.Host.UseSerilog();
                /*******************************加载模块************************************************/
                prev = watcher.ElapsedMilliseconds;
                Assemblies = SortModules(GetAssemblies());
                Log.Information($"加载模块完成，耗时{watcher.ElapsedMilliseconds - prev}毫秒");

                /*******************************加载类************************************************/
                //统计所有业务模块得
                prev = watcher.ElapsedMilliseconds;
                Assemblies.Where(c => c.ModuleInstance.Is<IModule>() && c.ModuleInstance.IsNot<FrameModule>() && c.ModuleInstance.IsNot<ComponentModule>()).Foreach(item => AllTypes.AddRange(item.Assembly.DefinedTypes));
                long time3 = watcher.ElapsedMilliseconds;
                Log.Information($"加载所有类完成，耗时{watcher.ElapsedMilliseconds - prev}毫秒");

                var model = new BootParams()
                {
                    Configuration = builder.Configuration,
                    Services = builder.Services,
                    Builder = builder,
                };
                /*******************************调用模块得PreInit方法（预处理）************************************************/
                prev = watcher.ElapsedMilliseconds;
                Assemblies.ForEach(item =>
                {
                    item.ModuleInstance.PreInit(model);
                });
                Log.Information($"模块预处理（PreInit)完成，耗时{watcher.ElapsedMilliseconds - prev}毫秒");
                /*******************************调用回调************************************************/
                prev = watcher.ElapsedMilliseconds;
                action?.Invoke();
                Log.Information($"回调完成，耗时{watcher.ElapsedMilliseconds - prev}毫秒");
                /*******************************调用模块得Initial方法（初始化）************************************************/
                Assemblies.ForEach(item => item.ModuleInstance.Init());
                long time6 = watcher.ElapsedMilliseconds;
                Log.Information($"模块初始化（Init)完成，耗时{watcher.ElapsedMilliseconds - prev}毫秒");
            }
            catch (Exception ex)
            {
                Log.Error("程序启动错误，请检查日志", ex);
                throw;
            }

        }

        public static void UseFramework(IApplicationBuilder app, Action action)
        {
            try
            {
                var watcher = new Stopwatch();
                watcher.Start();
                long time1 = watcher.ElapsedMilliseconds;
                /*******************************调用模块得PostInit方法************************************************/
                Assemblies.ForEach(item => item.ModuleInstance.PostInit(app));
                /*******************************Action方法************************************************/
                action?.Invoke();
                long time2 = watcher.ElapsedMilliseconds;
                Log.Information($"模块收尾工作完成（PostInit)，耗时{time2 - time1}毫秒");
            }
            catch (Exception ex)
            {
                Log.Error("程序启动错误，请检查日志", ex);
                throw;
            }
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="assemblies"></param>
        /// <returns></returns>
        public static List<ModuleInfo> SortModules(List<ModuleInfo> assemblies)
        {
            assemblies.Sort((t1, t2) =>
            {
                return (t1.ModuleInstance.Sort - t2.ModuleInstance.Sort);
            });
            return assemblies;
        }

        public static List<ModuleInfo> GetAssemblies()
        {
            var assemblies = new List<ModuleInfo>();
            var direcotory = AppDomain.CurrentDomain.BaseDirectory; //Directory.GetCurrentDirectory();
            var files = Directory.GetFiles(direcotory, "*.dll", SearchOption.AllDirectories);
            foreach (string path in files)
            {
                Log.Information($"正在加载：【{path}】");
                try
                {
                    Assembly assembly = Assembly.LoadFrom(path);
                    var tt = assembly.DefinedTypes.FirstOrDefault(type => typeof(IModule).IsAssignableFrom(type.AsType()));
                    if (assembly != null && assembly.DefinedTypes.Any(type => typeof(IModule).IsAssignableFrom(type.AsType()) && !type.IsAbstract))
                    {
                        var moduleType = assembly.DefinedTypes.FirstOrDefault(type => typeof(IModule).IsAssignableFrom(type.AsType()) && !type.IsAbstract);
                        if (moduleType != null && !string.IsNullOrWhiteSpace(moduleType.FullName))
                        {
                            var obj = assembly.CreateInstance(moduleType.FullName, true);
                            var instance = assembly.CreateInstance(moduleType.FullName)!.CastTo<IModule>();
                            var moduleInfo = new ModuleInfo() { Assembly = assembly, ModuleInstance = instance };
                            assemblies.Add(moduleInfo);
                        }

                    }
                }
                catch (Exception ex)
                {
                    Log.Error(ex.Message, ex);
                    continue;
                }
                Log.Information($"加载【{path}】完成");
            }
            return assemblies;
        }
    }

}
